
Imports System.Reflection
Imports System.Net
Imports System.IO
#If Not (PocketPC Or Smartphone) Then
Imports System.IO.Compression
#End If
Imports System.Text.RegularExpressions
#If Not (PocketPC OrElse Smartphone) Then
Imports System.Web
#End If
Imports std

Public Module httpUtils

    Public Const acceptSeperator As String = character.comma
    Public Const rangeHeadSeperator As String = character.equalSign
    Public Const rangeSeperator As String = character.comma
    Public Const rangeValueSeperator As String = character.minusSign

    Public Const metaRegex As String = "<meta([^<]*)charset=([^<]*)"""
    Public Const pathseperator As Char = network.pathSeperator
    Public Const httpAddressHeader As String = network.httpAddressHeader
    Public Const httpsAddressHeader As String = network.httpsAddressHeader
    Public Const protocolMark As Char = network.protocolMark
    Public Const gzipCompress As String = network.gzipCompress
    Public Const metaheader As String = network.metaheader
    Public ReadOnly metaheaderLen As Int32 = strlen(metaheader)

    Private Function friendlyStr(ByVal s As String) As String
        Return ifoper(isemptystring(s), "(null)", s)
    End Function

    Public Function availableAddress(ByVal address As String) As Boolean
        Return address.IndexOf(protocolMark) <> npos _
            OrElse address.IndexOf("&#" + Convert.ToString(Asc(protocolMark)) + ";") <> npos
    End Function

    Public Function availableHttpAddress(ByVal address As String, _
                                         Optional ByVal includeHttps As Boolean = True) As Boolean
        Return strsame(address, httpAddressHeader, httpAddressHeader.Length) _
            Or (includeHttps And strsame(address, httpsAddressHeader, httpsAddressHeader.Length))
    End Function

    Public Sub checkHttpAddress(ByRef address As String, _
                                Optional ByVal includeHttps As Boolean = True)
        If Not availableHttpAddress(address, includeHttps) Then
            address = network.httpAddressHeader + address
        End If
    End Sub

    Public Function checkHttpDeadlink(ByVal address As String, _
                                      Optional ByVal useProxy As Boolean = False, _
                                      Optional ByVal proxy As String = Nothing, _
                                      Optional ByVal checkTimes As Int32 = 5) As Boolean
        Dim response As HttpWebResponse = Nothing
        Dim i As Int32
        If isdebugmode() Then
            raiseError("check dead link of url = " + address, errorHandle.errorType.application)
        End If
        For i = 0 To checkTimes - 1
            If requestHttpWeb(address, response, useProxy, proxy) Then
                Exit For
            End If
        Next
        If Not response Is Nothing Then
            response.Close()
        End If
        Return (i = checkTimes)
    End Function

    Public Function requestHttpWeb(ByVal address As String, ByRef response As HttpWebResponse, _
                                   Optional ByVal useProxy As Boolean = False, _
                                   Optional ByVal proxy As String = Nothing, _
                                   Optional ByRef requestAddress As String = Nothing, _
                                   Optional ByVal timeoutMS As Int64 = npos, _
                                   Optional ByVal userAgent As String = navigator.userAgent.mozilla4, _
                                   Optional ByVal referer As String = Nothing, _
                                   Optional ByVal content As String = Nothing, _
                                   Optional ByVal contentEncoding As Text.Encoding = Nothing) As Boolean
        Dim request As HttpWebRequest = Nothing
        checkHttpAddress(address)
        If isdebugmode() Then
            raiseError("request webpage from url = " + address, errorHandle.errorType.application)
        End If
        Try
            request = HttpWebRequest.Create(address)
            If timeoutMS > 0 Then
                request.Timeout = timeoutMS
            End If
            requestAddress = Convert.ToString(request.Address)
            If useProxy AndAlso Not isemptystring(proxy) Then
                request.Proxy = New WebProxy(proxy)
            ElseIf Not useProxy Then
                request.Proxy = Nothing
            End If
            request.Referer = referer
            request.UserAgent = userAgent
            request.KeepAlive = False
            If Not isemptystring(content) Then
                request.Method = network.http.method.POST
                If contentEncoding Is Nothing Then
                    contentEncoding = encoding.utf8()
                End If
                Dim buff As bytes = Nothing
                buff = New bytes(contentEncoding.GetBytes(content))
                request.ContentLength = buff.size()
                If Not writestream(request.GetRequestStream(), +buff, holdtime:=timeoutMS) Then
                    raiseError(errorHandle.errorType.warning, "cannot write content body for request ", address)
                End If
                closestream(request.GetRequestStream())
            Else
                request.Method = network.http.method.GET
            End If
        Catch ex As Exception
            raiseError(errorHandle.errorType.exclamation, _
                       "unhandled exception caught during generate HttpWebRequest, ex ", ex.Message)
            Return False
        End Try

        Try
            response = request.GetResponse()
        Catch ex As WebException
            response = convertor.cast(Of HttpWebResponse)(ex.Response(), False)
        End Try

        Return Not (response Is Nothing)
    End Function

    Public Function pingHttpWeb(ByVal address As String, Optional ByVal useProxy As Boolean = False, _
                                Optional ByVal proxy As String = Nothing, _
                                Optional ByRef requestAddress As String = Nothing, _
                                Optional ByVal timeoutMS As Int64 = npos, _
                                Optional ByVal userAgent As String = navigator.userAgent.mozilla4, _
                                Optional ByVal referer As String = Nothing) As Boolean
        Dim response As HttpWebResponse = Nothing
        If requestHttpWeb(address, response, useProxy, proxy, requestAddress, timeoutMS, userAgent, referer) Then
            If Not response Is Nothing Then
                response.Close()
            End If
            Return True
        Else
            Return False
        End If
    End Function

    Public Function responseMetaCharSet(ByVal responseStream As Stream, ByRef charset As String) As Boolean
        If Not responseStream Is Nothing AndAlso responseStream.CanRead Then
            Dim responseReader As StreamReader = Nothing
            responseReader = New StreamReader(responseStream)
            Dim header As String = Nothing
            Dim readsize As Int32 = 512
            Dim readcount As Int32
            Dim readchars(readsize - 1) As Char
            readcount = responseReader.Read(readchars, 0, readsize)
            If readcount > 0 Then
                header = New String(readchars, 0, readcount)
                Dim charSetMatch As Match = Regex.Match(header, metaRegex _
                                                       , RegexOptions.IgnoreCase Or RegexOptions.Multiline)
                charset = charSetMatch.Groups(2).Value
            End If
            closestream(responseReader)
            Return Not isemptystring(charset)
        Else
            Return False
        End If
    End Function

    Private Function readHttpResponseWithEncodingCheck(ByVal response As HttpWebResponse _
                                                     , ByVal responseStream As Stream _
                                                     , ByRef result As String _
                                                     , ByVal address As String) As Boolean
        On Error Resume Next
        Dim responseMemory As MemoryStream = Nothing
        Dim d As bytes = Nothing
        If readstream(responseStream, d) Then
            responseMemory = New MemoryStream(+d)
        End If
        If Not responseMemory Is Nothing Then
            closestream(responseStream)
            responseStream = responseMemory

            Dim encoding As Text.Encoding = Nothing
            If encoding Is Nothing Then
                Dim charset As String = Nothing
                responseMemory = New MemoryStream(+d)
                If responseMetaCharSet(responseMemory, charset) Then
                    encoding = System.Text.Encoding.GetEncoding(charset)
                End If
                closestream(responseMemory)
            End If
            If encoding Is Nothing Then
                encoding = System.Text.Encoding.GetEncoding(response.ContentEncoding)
            End If
            If encoding Is Nothing Then
                encoding = System.Text.Encoding.GetEncoding(response.CharacterSet)
            End If
            Dim responseReader As StreamReader = Nothing
            If Not encoding Is Nothing Then
                responseReader = New StreamReader(responseStream, encoding)
            Else
                responseReader = New StreamReader(responseStream)
            End If
            copy(result, responseReader.ReadToEnd())
            closestream(responseReader)
            closestream(responseStream)
            response.Close()
            Return True
        Else
            raiseError("cannot get responseMemoryStream, url " + address, errorHandle.errorType.exclamation)
            closestream(responseStream)
            response.Close()
            Return False
        End If
    End Function

    Public Function readHttpResponse(ByVal response As HttpWebResponse _
                                     , ByRef result As String _
                                     , ByVal address As String _
                                     , Optional ByVal encoding As System.Text.Encoding = Nothing) As Boolean
        Dim responseStream As Stream
#If Not (PocketPC Or Smartphone) Then
        If strsame(response.ContentEncoding, gzipCompress, False) Then
            responseStream = New GZipStream(response.GetResponseStream, CompressionMode.Decompress)
        Else
            responseStream = response.GetResponseStream
        End If
#Else
        responseStream = response.GetResponseStream
#End If
        If Not responseStream Is Nothing Then
            If encoding Is Nothing Then
                Return readHttpResponseWithEncodingCheck(response, responseStream, result, address)
            Else
                Dim reader As StreamReader = Nothing
                reader = New StreamReader(responseStream, encoding)
                result = reader.ReadToEnd
                closestream(reader)
                response.Close()
                Return True
            End If
        Else
            raiseError("cannot get responseStream, url " + address, errorHandle.errorType.exclamation)
            response.Close()
            Return False
        End If
    End Function

    Public Function readHttpWeb(ByVal address As String, ByRef result As String, _
                                Optional ByVal encoding As System.Text.Encoding = Nothing, _
                                Optional ByVal useProxy As Boolean = False, _
                                Optional ByVal proxy As String = Nothing, _
                                Optional ByVal requestTimes As Int32 = 5, _
                                Optional ByRef requestAddress As String = Nothing, _
                                Optional ByRef responseAddress As String = Nothing, _
                                Optional ByVal timeoutMS As Int64 = npos) As Boolean
        On Error Resume Next
        Dim response As HttpWebResponse = Nothing
        Dim i As Int32
        result = emptyString

        For i = 0 To requestTimes - 1
            If requestHttpWeb(address, response, useProxy, proxy, requestAddress, timeoutMS) Then
                Exit For
            End If
        Next

        If i < requestTimes Then
            responseAddress = Convert.ToString(response.ResponseUri)
            If isdebugmode() Then
                raiseError("get response from server, url = " + address, errorHandle.errorType.application)
            End If
            Return readHttpResponse(response, result, address, encoding)
        Else
            If errorHandle.mode = errorHandle.displayMode.console Then
                raiseError("no response from server, url = " + address, errorHandle.errorType.exclamation)
            End If
            If Not response Is Nothing Then
                response.Close()
            End If
            Return False
        End If
    End Function

    'WML, refer to http://www.w3school.com.cn/wap/wml_reference.asp

    Public Function readHttpWebBypassWML(ByVal address As String, ByRef result As String, _
                                         Optional ByVal retrytimes As Int32 = 8, _
                                         Optional ByVal timeoutMS As Int64 = npos) As Boolean
        While retrytimes > 0
            If readHttpWeb(address, result, timeoutMS:=timeoutMS) Then
                Dim jump As Boolean = False
                Dim index As Int32
                index = strindexof(result, "<wml>")
                If index <> npos Then
                    index = result.IndexOf("<go", index)
                    If index <> npos Then
                        index = result.IndexOf("href=""", index)
                        If index <> npos Then
                            jump = True
                        End If
                    End If
                End If

                If Not jump Then
                    Exit While
                End If
            End If
            retrytimes -= 1
        End While

        Return retrytimes > 0
    End Function

    Public Function readHttpWeb(ByVal url As String, ByRef content As String, ByVal retrytimes As Int32, _
                                Optional ByVal timeoutMS As Int64 = npos) As Boolean
        While retrytimes > 0
            If readHttpWeb(url, content, timeoutMS:=timeoutMS) Then
                Exit While
            End If
            retrytimes -= 1
        End While

        Return retrytimes > 0
    End Function

    Public Function readHttpWebWithResponseUrl(ByRef url As String, ByRef content As String, _
                                               ByVal retrytimes As Int32, _
                                               Optional ByVal timeoutMS As Int64 = npos) As Boolean
        While retrytimes > 0
            If readHttpWeb(url, content, responseAddress:=url, timeoutMS:=timeoutMS) Then
                Exit While
            End If
            retrytimes -= 1
        End While

        Return retrytimes > 0
    End Function

#If Not (PocketPC OrElse Smartphone) Then

    Public Function saveWebToMHT(ByVal address As String, ByVal outputFile As String) As Boolean
        On Error Resume Next
        Dim msg As New CDO.Message
        checkHttpAddress(address)
        raiseError("save webpage to mht from url = " + address)
        msg.Configuration = New CDO.Configuration
        msg.CreateMHTMLBody(address, CDO.CdoMHTMLFlags.cdoSuppressAll, _
                            defaultValue.emptyString, defaultValue.emptyString)
        msg.GetStream.SaveToFile(outputFile, ADODB.SaveOptionsEnum.adSaveCreateOverWrite)

        Return Not msg.GetStream Is Nothing
    End Function

    Public Function accept(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return accept(context.Request)
    End Function

    Public Function accept(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Return request.Headers(network.http.headers.Accept)
    End Function

    Public Function isBrowserUserAgent(ByVal context As HttpListenerContext) As Boolean
        On Error Resume Next
        Return isBrowserUserAgent(context.Request)
    End Function

    Public Function isBrowserUserAgent(ByVal request As HttpListenerRequest) As Boolean
        On Error Resume Next
        Return isBrowserUserAgent(userAgent(request))
    End Function
#End If

    Public Function isBrowserUserAgent(ByVal userAgent As String) As Boolean
        On Error Resume Next
        Return strcont(userAgent, "Mozilla", False) OrElse strcont(userAgent, "Opera", False)
    End Function

#If RETIRED Then
    Public Function isIEUserAgent(ByVal context As httpContext) As Boolean
        On Error Resume Next
        Return context.isIEUserAgent()
    End Function
#End If

#If Not (PocketPC OrElse Smartphone) Then
    Public Function isIEUserAgent(ByVal context As HttpListenerContext) As Boolean
        On Error Resume Next
        Return isIEUserAgent(context.Request)
    End Function

    Public Function isIEUserAgent(ByVal request As HttpListenerRequest) As Boolean
        On Error Resume Next
        Return isIEUserAgent(userAgent(request))
    End Function

    Public Function userAgent(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return userAgent(context.Request)
    End Function

    'this is a workaround, cannot get UserAgent() if the HttpListenerResponse has been closed.
    Private Class _useragent
        <ThreadStatic()> Public Shared request As HttpListenerRequest = Nothing
        <ThreadStatic()> Public Shared ua As String = Nothing
    End Class
    Public Function userAgent(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        If Not ReferenceEquals(request, _useragent.request) Then
            _useragent.ua = Nothing
            _useragent.request = request
            _useragent.ua = request.UserAgent()
        End If
        Return _useragent.ua
    End Function

    Public Function friendlyUserAgent(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return friendlyUserAgent(context.Request)
    End Function

    Public Function friendlyUserAgent(ByVal request As HttpListenerRequest) As String
        Return friendlyUserAgent(userAgent(request))
    End Function

    Public Function friendlyUserAgent(ByVal s As String) As String
        Return friendlyStr(s)
    End Function

    Public Function requestHostPort(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return requestHostPort(context.Request)
    End Function

    Public Function requestHostPort(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Return request.Url().Authority()
    End Function

    Public Function requestHost(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return requestHost(context.Request)
    End Function

    Public Function removeport(ByRef s As String) As String
        strseg(s, s, Nothing, network.portMark)
        Return s
    End Function

    Public Function requestHost(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Dim rtn As String = Nothing
        rtn = requestHostPort(request)
        removeport(rtn)
        Return rtn
    End Function

    Public Function requestPathQuery(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return requestPathQuery(context.Request)
    End Function

    Public Function requestPathQuery(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Return request.Url.PathAndQuery
    End Function

    Public Function requestFullUrl(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return requestFullUrl(context.Request)
    End Function

    Public Function requestFullUrl(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Dim rtn As String = Nothing
        copy(rtn, request.Url().AbsoluteUri())
        Dim host As String = Nothing
        host = requestHostPort(request)
        Dim index As Int64
        index = strindexof(rtn, host)
        If index <> npos Then
            rtn = strleft(rtn, index + strlen(host)) + request.RawUrl()
        End If
        Return rtn
    End Function

    Public Function requestResource(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return requestResource(context.Request)
    End Function

    Public Function requestResource(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Return requestResource(requestFullUrl(request))
    End Function

    Public Function referer(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return referer(context.Request)
    End Function

    'this is a workaround, cannot get UrlReferer() if the HttpListenerResponse has been closed.
    Private Class _referer
        <ThreadStatic()> Public Shared request As HttpListenerRequest = Nothing
        <ThreadStatic()> Public Shared ref As String = Nothing
    End Class

    Public Function referer(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        If Not ReferenceEquals(request, _referer.request) Then
            _referer.ref = Nothing
            _referer.request = request
            _referer.ref = request.UrlReferrer().AbsoluteUri()
        End If
        Return _referer.ref
    End Function

    Public Function friendlyReferer(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return friendlyReferer(context.Request)
    End Function

    Public Function friendlyReferer(ByVal request As HttpListenerRequest) As String
        Return friendlyReferer(referer(request))
    End Function

    Public Function friendlyReferer(ByVal s As String) As String
        Return friendlyStr(s)
    End Function

    Public Function requestIp(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return requestIp(context.Request)
    End Function

    Public Function requestIp(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Return Convert.ToString(request.RemoteEndPoint.Address.ToString())
    End Function

    Public Function localIp(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return localIp(context.Request)
    End Function

    Public Function localIp(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Return Convert.ToString(request.LocalEndPoint.Address.ToString())
    End Function

    Public Function responseIp(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return responseIp(context.Request)
    End Function

    Public Function responseIp(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Dim rtn As String = Nothing
        copy(rtn, request.UserHostAddress())
        removeport(rtn)
        Return rtn
    End Function

    Public Function acceptEncoding(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return acceptEncoding(context.Request)
    End Function

    Public Function acceptEncoding(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Dim rtn As String = Nothing
        copy(rtn, request.Headers(network.http.headers.AcceptEncoding))
        Return rtn
    End Function

    Public Function acceptGzip(ByVal s As String) As Boolean
        On Error Resume Next
        Return strcont(s, gzipCompress)
    End Function

    Public Function acceptGzip(ByVal context As HttpListenerContext) As Boolean
        On Error Resume Next
        Return acceptGzip(context.Request)
    End Function

    Public Function acceptGzip(ByVal request As HttpListenerRequest) As Boolean
        On Error Resume Next
        Return acceptGzip(acceptEncoding(request))
    End Function

    Public Function publicIps(ByVal host As String, Optional ByVal forceUpdate As Boolean = False) As vector(Of String)
        Return dnscache.resolveAsString(host, forceUpdate)
    End Function

    Public Function publicIps(ByVal context As HttpListenerContext, _
                              Optional ByVal forceUpdate As Boolean = False) As vector(Of String)
        On Error Resume Next
        Return publicIps(context.Request, forceUpdate)
    End Function

    Public Function publicIps(ByVal request As HttpListenerRequest, _
                              Optional ByVal forceUpdate As Boolean = False) As vector(Of String)
        On Error Resume Next
        Return publicIps(requestHost(request), forceUpdate)
    End Function

    Public Function isPublicIp(ByVal context As HttpListenerContext, _
                               ByVal ip As String, _
                               Optional ByVal forceUpdate As Boolean = False) As Boolean
        On Error Resume Next
        Return isPublicIp(publicIps(context, forceUpdate), ip)
    End Function

    Public Function isPublicIp(ByVal request As HttpListenerRequest, _
                               ByVal ip As String, _
                               Optional ByVal forceUpdate As Boolean = False) As Boolean
        On Error Resume Next
        Return isPublicIp(publicIps(request, forceUpdate), ip)
    End Function

    Public Function isPublicIp(ByVal host As String, _
                               ByVal ip As String, _
                               Optional ByVal forceUpdate As Boolean = False) As Boolean
        On Error Resume Next
        Return isPublicIp(publicIps(host, forceUpdate), ip)
    End Function

    Private Function isPublicIp(ByVal publicIps As vector(Of String), _
                                ByVal ip As String) As Boolean
        If Not publicIps Is Nothing Then
            For i As Int64 = 0 To publicIps.size() - 1
                If strsame(publicIps(i), ip, False) Then
                    Return True
                End If
            Next
        End If

        Return False
    End Function

    Public Function isInternalRequest(ByVal localIp As String, ByVal requestIp As String, _
                                      ByVal responseIp As String, ByVal requestHost As String) As Boolean
        On Error Resume Next
        Return strsame(localIp, requestIp, False) OrElse _
               strsame(responseIp, requestIp, False) OrElse _
               isPublicIp(requestHost, requestIp)
    End Function

    Public Function isInternalRequest(ByVal context As HttpListenerContext) As Boolean
        On Error Resume Next
        Return isInternalRequest(context.Request)
    End Function

    Public Function isInternalRequest(ByVal request As HttpListenerRequest) As Boolean
        On Error Resume Next
        Return isInternalRequest(localIp(request), requestIp(request), responseIp(request), requestHost(request))
    End Function

    Public Function language(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return language(context.Request)
    End Function

    Public Function language(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Return request.Headers(network.http.headers.AcceptLanguage)
    End Function

    Public Function traceID(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return traceID(context.Request)
    End Function

    Public Function traceID(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Return Convert.ToString(request.RequestTraceIdentifier)
    End Function

    Public Function expect(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return expect(context.Request)
    End Function

    Public Function expect(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Return request.Headers(network.http.headers.Expect)
    End Function

    Public Function ifModifiedSince(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return ifModifiedSince(context.Request)
    End Function

    Public Function ifModifiedSince(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Return request.Headers(network.http.headers.IfModifiedSince)
    End Function

    Public Function range(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return range(context.Request)
    End Function

    Public Function range(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Return request.Headers(network.http.headers.Range)
    End Function

    Public Function rangeSeries(ByVal context As HttpListenerContext) _
                                                                    As pair(Of String, vector(Of pair(Of Int64, Int64)))
        On Error Resume Next
        Return rangeSeries(context.Request)
    End Function

    Public Function rangeSeries(ByVal request As HttpListenerRequest) _
                                                                    As pair(Of String, vector(Of pair(Of Int64, Int64)))
        On Error Resume Next
        Return rangeSeries(range(request))
    End Function

    Public Function transferEncoding(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return transferEncoding(context.Request)
    End Function

    Public Function transferEncoding(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        Return request.Headers(network.http.headers.headers.TransferEncoding)
    End Function

    Public Function chunkedTransfer(ByVal te As String) As Boolean
        Return strsame(te, network.http.headers.transferEncoding.chunked, False)
    End Function

    Public Function chunkedTransfer(ByVal request As HttpListenerRequest) As Boolean
        On Error Resume Next
        Return chunkedTransfer(transferEncoding(request))
    End Function

    Public Function chunkedTransfer(ByVal context As HttpListenerContext) As Boolean
        On Error Resume Next
        Return chunkedTransfer(context.Request)
    End Function

    Public Function contentType(ByVal context As HttpListenerContext) As String
        On Error Resume Next
        Return contentType(context.Request)
    End Function

    'this is a workaround, cannot get ContentType() if the HttpListenerResponse has been closed.
    Private Class _contentType
        <ThreadStatic()> Public Shared request As HttpListenerRequest = Nothing
        <ThreadStatic()> Public Shared ct As String = Nothing
    End Class
    Public Function contentType(ByVal request As HttpListenerRequest) As String
        On Error Resume Next
        If Not ReferenceEquals(request, _contentType.request) Then
            _contentType.ct = Nothing
            _contentType.request = request
            _contentType.ct = request.ContentType()
        End If
        Return _contentType.ct
    End Function

    Private ReadOnly _addheader As MethodInfo = _
        GetType(WebHeaderCollection).GetMethod("AddWithoutValidate", _
                                               BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.InvokeMethod)

    Public Sub unsafeAddHeader(ByVal coll As WebHeaderCollection, ByVal name As String, ByVal value As String)
        _addheader.Invoke(coll, New Object() {name, value})
    End Sub

    Private Sub unsafeAddHeaderCB(ByVal name As String, ByVal value As String, ByVal para As Object)
        unsafeAddHeader(convertor.cast(Of WebHeaderCollection)(para), name, value)
    End Sub

    Public Sub copyHttpHeaders(ByVal inheaders As WebHeaderCollection, _
                               ByVal outheaders As WebHeaderCollection)
        nvchelper.traversal(inheaders, AddressOf unsafeAddHeaderCB, outheaders)
    End Sub

    Public Sub copyHttpHeaders(ByVal m As map(Of String, list(Of String)), ByVal o As WebHeaderCollection)
        nvchelper.copyToNVC(m, o, AddressOf unsafeAddHeaderCB)
    End Sub

    'http headers referer http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

    Public Function copyHttpRequestHeaders(ByVal inRequest As HttpListenerRequest, _
                                           ByVal outRequest As HttpWebRequest) As Boolean
        If debugassert(Not inRequest Is Nothing AndAlso Not outRequest Is Nothing, _
                       "inRequst or outRequest is nothing.") Then
            On Error Resume Next

            copyHttpHeaders(inRequest.Headers, outRequest.Headers)

            If chunkedTransfer(inRequest) Then
                outRequest.SendChunked = True
            End If

            'IT'S A KNOWN ISSUE IN .NET FRAMEWORK, THE USER AGENT IS MISSING IN HTTPLISTENERREQUEST.HEADERS
            outRequest.UserAgent = userAgent(inRequest)

#If RETIRED Then
            'alpha order
            outRequest.Accept = accept(inRequest)
            outRequest.ContentLength = inRequest.ContentLength64
            outRequest.ContentType = inRequest.ContentType
            outRequest.Expect = expect(inRequest)
            outRequest.IfModifiedSince = ifModifiedSince(inRequest)
            outRequest.KeepAlive = inRequest.KeepAlive
            outRequest.Referer = referer(inRequest)

            Dim series As pair(Of String, vector(Of pair(Of Int64, Int64))) = Nothing
            series = rangeSeries(inRequest)
            If Not series Is Nothing Then
                Dim ranges As vector(Of pair(Of Int64, Int64)) = Nothing
                ranges = series.second
                If Not ranges Is Nothing AndAlso ranges.size() > 0 Then
                    Dim i As Int64
                    For i = 0 To ranges.size() - 1
                        Dim first As Int32
                        Dim second As Int32
                        If Not convertor.toint32(ranges(i).first, first) _
                            OrElse Not convertor.toint32(ranges(i).second, second) Then
                            raiseError("cannot convert ranges to int32, invalid request.", errorHandle.errorType.user)
                        Else
                            If first = npos Then
                                outRequest.AddRange(series.first, -second)
                            ElseIf second = npos Then
                                outRequest.AddRange(series.first, first)
                            Else
                                outRequest.AddRange(series.first, first, second)
                            End If
                        End If
                    Next
                End If
            End If

            outRequest.UserAgent = userAgent(inRequest)
#End If

            Return True
        Else
            Return False
        End If
    End Function

    Public Function copyHttpResponseHeaders(ByVal inResponse As HttpWebResponse, _
                                            ByVal outResponse As HttpListenerResponse) As Boolean
        If debugassert(Not inResponse Is Nothing AndAlso Not outResponse Is Nothing, _
                       "inResponse or outResponse is nothing.") Then
            copyHttpHeaders(inResponse.Headers, outResponse.Headers)
            If contentLengthValid(inResponse) Then
                outResponse.ContentLength64 = inResponse.ContentLength
            End If
            Dim s As String = Nothing
            s = inResponse.Headers(HttpResponseHeader.KeepAlive)
            If Not s Is Nothing Then
                outResponse.KeepAlive = strsame(s, "true", False)
            End If
            s = inResponse.Headers(HttpResponseHeader.Connection)
            If Not s Is Nothing AndAlso Not outResponse.KeepAlive Then
                outResponse.KeepAlive = strsame(s, network.http.headers.connection.keep_alive, False)
            End If
            outResponse.SendChunked = strsame(inResponse.Headers(HttpResponseHeader.TransferEncoding), "chunked", False)
            Return True
        Else
            Return False
        End If
    End Function
#End If

    Public Function contentLengthValid(ByVal i As Int64) As Boolean
        Return i >= 0
    End Function

    Public Function contentLengthValid(ByVal inResponse As HttpWebResponse) As Boolean
        If inResponse Is Nothing Then
            Return False
        Else
            Return contentLengthValid(inResponse.ContentLength)
        End If
    End Function

    Public Function isIEUserAgent(ByVal userAgent As String) As Boolean
        On Error Resume Next
        Return strcont(userAgent, "msie", False)
    End Function

    Public Function requestResource(ByVal str As String) As String
        On Error Resume Next
        Dim index As Int64
        index = strindexof(str, network.questionMark, False)
        If index = npos Then
            Return str
        Else
            Return strleft(str, index)
        End If
    End Function

    Public Function rangeSeries(ByVal header As String) As pair(Of String, vector(Of pair(Of Int64, Int64)))
        Dim rtn As vector(Of pair(Of Int64, Int64)) = Nothing
        Dim rangeSpecifier As String = Nothing
        If Not isemptystring(header) Then
            Dim index As Int64
            index = strindexof(header, rangeHeadSeperator)
            If index <> npos Then
                rangeSpecifier = strleft(header, index)
                Dim words As vector(Of String) = Nothing
                strsepar(strmid(header, index + 1), rangeSeperator + rangeValueSeperator, words)
                If Not words Is Nothing AndAlso words.size() > 0 Then
                    'odd, then add an empty string to make the follow logic easy
                    If (words.size() And 1) = 1 Then
                        words.push_back(emptyString)
                    End If
                    rtn = New vector(Of pair(Of Int64, Int64))()
                    Dim i As Int64
                    For i = 0 To words.size() - 2 Step 2
                        Dim first As Int64
                        Dim second As Int64
                        If isemptystring(words(i)) Then
                            first = npos
                        ElseIf Not convertor.toint64(words(i), first) Then
                            'not a valid range string
                            raiseError("cannot convert " + words(i) + " to int64, invalid request. range " + header _
                                       , errorHandle.errorType.user)
                            Continue For
                        End If
                        If isemptystring(words(i + 1)) Then
                            second = npos
                        ElseIf Not convertor.toint64(words(i + 1), second) Then
                            'not a valid range string
                            raiseError("cannot convert " + words(i + 1) + " to int64, invalid request. range " + header _
                                       , errorHandle.errorType.user)
                            Continue For
                        End If

                        rtn.push_back(make_pair(first, second))
                    Next
                End If
            End If
        End If

        Return make_pair(rangeSpecifier, rtn)
    End Function

#If Not PocketPC AndAlso Not Smartphone Then
    Public Function addheader(ByVal dest As WebHeaderCollection, _
                              ByVal name As HttpResponseHeader, ByVal value As String) As Boolean
        If debugassert(Not dest Is Nothing, "dest is nothing.") Then
            Try
                dest.Add(name, value)
                Return True
            Catch ex As Exception
                raiseError("cannot add HttpResponseHeader <" + Convert.ToString(name) + "> " + _
                           "to WebHeaderCollection, ex " + ex.Message, errorHandle.errorType.system)
                Return False
            End Try
        Else
            Return False
        End If
    End Function

    Public Function removeheader(ByVal dest As WebHeaderCollection, ByVal name As HttpResponseHeader) As Boolean
        If debugassert(Not dest Is Nothing, "dest is nothing.") Then
            Try
                dest.Remove(name)
                Return True
            Catch ex As Exception
                raiseError("cannot remove HttpResponseHeader <" + Convert.ToString(name) + "> " + _
                           "from WebHeaderCollection, ex " + ex.Message, errorHandle.errorType.system)
                Return False
            End Try
        Else
            Return False
        End If
    End Function

    Public Function modifyheader(ByVal dest As WebHeaderCollection, _
                                 ByVal name As HttpResponseHeader, ByVal value As String) As Boolean
        Return removeheader(dest, name) AndAlso addheader(dest, name, value)
    End Function

    Public Function addheader(ByVal dest As WebHeaderCollection, _
                              ByVal name As HttpRequestHeader, ByVal value As String) As Boolean
        If debugassert(Not dest Is Nothing, "dest is nothing.") Then
            Try
                dest.Add(name, value)
                Return True
            Catch ex As Exception
                raiseError("cannot add HttpRequestHeader <" + Convert.ToString(name) + "> " + _
                           "to WebHeaderCollection, ex " + ex.Message, errorHandle.errorType.system)
                Return False
            End Try
        Else
            Return False
        End If
    End Function

    Public Function removeheader(ByVal dest As WebHeaderCollection, ByVal name As HttpRequestHeader) As Boolean
        If debugassert(Not dest Is Nothing, "dest is nothing.") Then
            Try
                dest.Remove(name)
                Return True
            Catch ex As Exception
                raiseError("cannot remove HttpRequestHeader <" + Convert.ToString(name) + "> " + _
                           "from WebHeaderCollection, ex " + ex.Message, errorHandle.errorType.system)
                Return False
            End Try
        Else
            Return False
        End If
    End Function

    Public Function modifyheader(ByVal dest As WebHeaderCollection, _
                                 ByVal name As HttpRequestHeader, ByVal value As String) As Boolean
        Return removeheader(dest, name) AndAlso addheader(dest, name, value)
    End Function
#End If

    Public Function addheader(ByVal dest As WebHeaderCollection, _
                              ByVal name As String, ByVal value As String) As Boolean
        Return nvchelper.add(dest, name, value)
    End Function

    Public Function removeheader(ByVal dest As WebHeaderCollection, ByVal name As String) As Boolean
        Return nvchelper.remove(dest, name)
    End Function

    Public Function modifyheader(ByVal dest As WebHeaderCollection, _
                                 ByVal name As String, ByVal value As String) As Boolean
        Return nvchelper.modify(dest, name, value)
    End Function
End Module
